package com.gopay.bis.manager.bank.expay;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

import com.gopay.bis.common.util.BankLogUtils;
import com.gopay.bis.common.util.DateUtils;
import com.gopay.bis.dao.bankorder.BankContractInfoDAO;
import com.gopay.bis.dao.bankorder.BankOrderDAO;
import com.gopay.bis.dao.genreceiptorder.CpsGeneralReceiptOrderDAO;
import com.gopay.common.Result;
import com.gopay.common.cipher.utils.LoggerMaskUtils;
import com.gopay.common.constants.bank.BankConstants;
import com.gopay.common.constants.bank.BisBankBusinessProd;
import com.gopay.common.domain.bank.BankOrder;
import com.gopay.common.domain.bank.BaseMessage;
import com.gopay.common.domain.bank.ExPayBaseMessage;
import com.gopay.common.domain.bank.pab.expay.PabExPayMessage;
import com.gopay.common.domain.bank.pab.expay.req.PabExPayMessageCPReq;
import com.gopay.common.domain.bank.pab.expay.req.PabExPayMessageFPAReq;
import com.gopay.common.domain.bank.pab.expay.req.PabExPayMessageFSPReq;
import com.gopay.common.domain.bank.pab.expay.req.PabExPayMessageSOQReq;
import com.gopay.common.domain.bank.pab.expay.req.PabExPayMessageSOQReqH;
import com.gopay.common.domain.bank.pab.expay.req.req.FastpayCPReq;
import com.gopay.common.domain.bank.pab.expay.req.req.FastpayFPAReq;
import com.gopay.common.domain.bank.pab.expay.req.req.FastpayFSPReq;
import com.gopay.common.domain.bank.pab.expay.req.req.FastpaySOQReq;
import com.gopay.common.domain.bank.pab.expay.req.req.MessageCPReq;
import com.gopay.common.domain.bank.pab.expay.req.req.MessageFPAReq;
import com.gopay.common.domain.bank.pab.expay.req.req.MessageFSPReq;
import com.gopay.common.domain.bank.pab.expay.req.req.MessageSOQReq;
import com.gopay.common.domain.bank.pab.expay.res.msg.SignFastpayCPRes;
import com.gopay.common.domain.bank.pab.expay.res.msg.SignFastpayFPARes;
import com.gopay.common.domain.bank.pab.expay.res.msg.SignFastpayFSPRes;
import com.gopay.common.domain.bank.pab.expay.res.msg.SignFastpaySOQRes;
import com.gopay.common.domain.bis.BisBankContractInfo;
import com.gopay.common.domain.cps.CpsGenReceiptOrder;
import com.gopay.common.domain.user.mcc.UserMccTransformReq;
import com.gopay.common.domain.user.mcc.UserMccTransformRes;
import com.gopay.common.exception.GopayException;
import com.gopay.common.user.mcc.manager.UserMccTransformManager;
import com.gopay.common.util.CommonUtils;
import com.gopay.remote.bank.businesses.ExPayService;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.CompactWriter;

/**
 * 
 * @ClassName: PabExPayManager
 * @Description: TODO
 * @author: ShenJun
 * @date: 2016年10月13日 下午5:38:09
 */
public class PabExPayManager {

	@Resource(name = "pabExPayServiceClient")
	private ExPayService pabExPayServiceClient;
	@Resource(name = "pabConfig")
	private Map<String, String> pabConfig;
	@Resource(name = "bankOrderDAO")
	private BankOrderDAO bankOrderDAO;	
	@Resource(name = "cpsGeneralReceiptOrderDAO")
	private CpsGeneralReceiptOrderDAO cpsGeneralReceiptOrderDAO;
	@Resource(name = "bankContractInfoDAO")
    private BankContractInfoDAO bankContractInfoDAO;
//	@Resource(name = "userMccTransformManager")
//	private UserMccTransformManager userMccTransformManager;
	//@Resource(name = "custCorpInfoDao")
	//private CustCorpInfoDao custCorpInfoDao;
	//@Resource(name = "cpsGenMainOrderDAO")
	//private CpsGenMainOrderDAO cpsGenMainOrderDAO;
	//@Resource(name = "userMccTransformManager")
	//private UserMccTransformManager userMccTransformManager;
	
	private final static Logger logger = LoggerFactory.getLogger("PabExPayManager");

	/**
	 * 平台发送实名身份认证请求
	 * 
	 * @param message
	 * @return
	 * @throws GopayException
	 */
	public Result certification(PabExPayMessage msg) throws GopayException {
		PabExPayMessage message = (PabExPayMessage) msg;
		FastpayFPAReq fastpay = this.buildFPAReqMsg(message);
		
		String plainStr = objToXml(fastpay, FastpayFPAReq.class);
		message.setPlainMessage(plainStr);

		BankLogUtils.printExPayManagerCertificationSendLog(message);
		Result res = (Result)pabExPayServiceClient.certification(message);
		printResult(res, "certification");
		return res;
	}

	/**
	 * 支付请求
	 * 
	 * @param msg
	 * @return
	 * @throws GopayException
	 */
	public Result payment(BaseMessage msg) throws GopayException {
		PabExPayMessage message = (PabExPayMessage) msg;
		FastpayCPReq fastpay = this.buildCpReqMsg(message);
		
		String plainStr = objToXml(fastpay, FastpayCPReq.class);
		message.setPlainMessage(plainStr);
		
		BankLogUtils.printExPayManagerPaymentSendLog(message);
		Result res = (Result)pabExPayServiceClient.payment(message);
		printResult(res, "payment");
		return res;
	}
	


	/**
	 * 发送交易查询请求
	 * 
	 * @param baseMsg
	 * @return
	 * @throws GopayException
	 */
	public Result query(BaseMessage baseMsg) throws GopayException {
		PabExPayMessage message = (PabExPayMessage) baseMsg;
		
	    FastpaySOQReq fastpay = this.buildSoqReqMsg(message);
	    
        String plainStr = objToXml(fastpay, FastpaySOQReq.class);
        message.setPlainMessage(plainStr);
        
        BankLogUtils.printExPayManagerQuerySendLog(message);
        Result res = (Result)pabExPayServiceClient.queryOrder(message);
        printResult(res, "query");
        return res;
		
		
	}

	public Result refund(BaseMessage baseMsg) throws GopayException {
		PabExPayMessage message = (PabExPayMessage) baseMsg;
		
	    FastpayFSPReq fastpay = this.buildFSPReqMsg(message);
	    
        String plainStr = objToXml(fastpay, FastpayFSPReq.class);
        message.setPlainMessage(plainStr);
        
        BankLogUtils.printExPayManagerQuerySendLog(message);
        Result res = (Result) pabExPayServiceClient.refund(message);
        printResult(res, "refund");
        return res;
		
		
	}
	
	/**
	 * 构建退款明文对象
	 * 
	 * @param message
	 * @return
	 * @throws GopayException 
	 */
	private FastpayFSPReq buildFSPReqMsg(PabExPayMessage message) throws GopayException {
		
		List<BankOrder> oriBankOrderList = bankOrderDAO.getBankOrdersByGopayOrderId(message.getTkOriBankOrderId());
		if(oriBankOrderList == null || oriBankOrderList.size() == 0){
			throw new GopayException("没有查询到原交易记录");
		}
		
		BankOrder oriBankOrder  = oriBankOrderList.get(0);
		String tokenNo = oriBankOrder.getBankOrderId();
		
		FastpayFSPReq fastpay = new FastpayFSPReq();
		MessageFSPReq msg = new MessageFSPReq();
		PabExPayMessageFSPReq FSPReq = new PabExPayMessageFSPReq();
		BeanUtils.copyProperties(message, FSPReq);
		FSPReq.setInstId(message.getMerID());//机构标识
		FSPReq.setTranCode(pabConfig.get("PAB.EXPAY.TRANCODE"));//交易码
		FSPReq.setSerialNo(message.getOrderId());//流水号
//		FSPReq.setDate(DateUtils.changeDateString(message.getOrderDate(), "yyyyMMddHHmmss", "yyyyMMdd HH:mm:ss"));//交易日期和时间
		FSPReq.setDate(DateUtils.date2string(new Date(), "yyyyMMdd HH:mm:ss"));//交易日期和时间
		FSPReq.setRefundAmt(message.getAmount().toString());//退款金额
		FSPReq.setOrgSerialNo(message.getTkOriBankOrderId());//原流水号
		FSPReq.setOrgDate(DateUtils.date2string(oriBankOrder.getSendBankTm(), "yyyyMMdd HH:mm:ss"));//原交易日期
		FSPReq.setSignNo(tokenNo);//卡代号

		msg.setId(this.getStringid());
		msg.setFSPReq(FSPReq);
		fastpay.setMessage(msg);
		
		return fastpay;
	}
	


	/**
	 * 构建客户验证签名明文对象
	 * 
	 * @param message
	 * @return
	 */
	public FastpayFPAReq buildFPAReqMsg(PabExPayMessage message) {
		FastpayFPAReq fastpay = new FastpayFPAReq();
		MessageFPAReq msg = new MessageFPAReq();
		PabExPayMessageFPAReq fpaReq = new PabExPayMessageFPAReq();
		BeanUtils.copyProperties(message, fpaReq);
		fpaReq.setInstId(message.getMerID());
		fpaReq.setName(message.getCardHolder());
		fpaReq.setDate(CommonUtils.formatDate(new Date(), "yyyyMMdd HH:mm:ss"));
		if ("01".equals(message.getIdentityType())) {
			fpaReq.setCertType("1");
		}
		fpaReq.setCertNo(message.getIdentityCode());
		fpaReq.setCardType("D");
		fpaReq.setMobilePhone(message.getMediaId());

		msg.setId(this.getStringid());
		msg.setfPAReq(fpaReq);
		fastpay.setMessage(msg);
		
		return fastpay;
	}

	/**
	 * 构建快捷支付签名明文对象
	 * 
	 * @param message
	 * @return
	 */
	public FastpayCPReq buildCpReqMsg(PabExPayMessage message) {
		//根据订单号查询出商户编号
		//CpsGenMainOrder cpsGenMainOrder = cpsGenMainOrderDAO.getCpsGenMainOrderByOrderId(message.getOrderId());
		//String custId = cpsGenMainOrder.getMerId();
		//.根据商户编号查询来获取商户信息
		//CustCorpInfo custCorpInfo = custCorpInfoDao.getByCustId(custId);
		
		String tokenNo = getTokenNo(message);
		
	    FastpayCPReq fastpay = new FastpayCPReq();
		MessageCPReq msg = new MessageCPReq();
		PabExPayMessageCPReq cpReq = new PabExPayMessageCPReq();
		cpReq.setInstId(message.getMerID());//机构标识
		cpReq.setSerialNo(message.getOrderId());//流水号
		cpReq.setDate(DateUtils.changeDateString(message.getOrderDate(), "yyyyMMddHHmmss", "yyyyMMdd HH:mm:ss"));//交易日期和时间
		cpReq.setSignNo(tokenNo);//卡代号
		cpReq.setAmount(message.getAmount().toString());//交易金额
		cpReq.setCurrency(pabConfig.get("PAB.EXPAY.CURRENCY"));//交易货币代码
		
		//cpReq.setPyerNm(message.getCardHolder());//付款方名称
		cpReq.setPyeeNm("国付宝信息科技有限公司");//收款方名称
		cpReq.setPyeeAcctId("11012296147101");//收款方账户
		cpReq.setPyeeAcctTp("0");//收款方账户类型
		cpReq.setPyeeAcctIssrId(message.getMerID());//收款方账户所属机构标识
		//***********先给个默认值 start
//		UserMccTransformReq userM = new UserMccTransformReq();
//		userM.setBankCode(BankConstants.BANK_LOGO_ABC);
//		userM.setCustId(message.getSpecial2());
//		UserMccTransformRes userRes = userMccTransformManager.graspUserMccInfo(userM);
		cpReq.setMrchntTp("1");//商户类型 	默认法人
		cpReq.setMrchntCertTp("11");//商户证件类型 	默认营业执照
		cpReq.setMrchntCertId("0000000000");//商户证件编码.
		//************先给个默认值 end
		cpReq.setOrdrDesc(message.getOrderId());//订单详情
		
		msg.setId(this.getStringid());
		msg.setCPReq(cpReq);
		fastpay.setMessage(msg);
		return fastpay;
	}

	/**
	 * 构建单笔查询签名明文对象
	 * 
	 * @param message
	 * @return
	 * @throws GopayException 
	 */
	public FastpaySOQReq buildSoqReqMsg(PabExPayMessage message) throws GopayException {
		
		//根据订单号查询出订单时间
		List<BankOrder> orderList = bankOrderDAO.getBankOrdersByGopayOrderId(message.getOrderId());
		BankOrder order = orderList.get(0);
		message.setOrderDate(DateUtils.date2string(order.getCreatedTs(), "yyyyMMddHHmmss"));
		
		//根据国付宝订单号获取token号
		String orderId = "";
		if(BisBankBusinessProd._12.value.equals(message.getBankBusProd().value)){
			orderId = message.getTkOriBankOrderId();
		}else{
			orderId = message.getOrderId();
		}
		List<BankOrder> oriBankOrderList = bankOrderDAO.getBankOrdersByGopayOrderId(orderId);
		if(oriBankOrderList == null || oriBankOrderList.size() == 0){
			throw new GopayException("没有查询到原交易记录");
		}
		BankOrder oriBankOrder  = oriBankOrderList.get(0);
		String tokenNo = oriBankOrder.getBankOrderId();
		
		FastpaySOQReq fastpay = new FastpaySOQReq();
		MessageSOQReq msg = new MessageSOQReq();
		/*
         * 如果交易时间为当天，就用单笔查询报文SOQReq，如果不是当天就用单笔历史查询报文SOQReqH
         */
		if(DateUtils.isToday(message.getOrderDate())){
		    PabExPayMessageSOQReq req = new PabExPayMessageSOQReq();
	        req.setInstId(message.getMerID());
	        req.setSerialNo(message.getOrderId());
	        req.setSignNo(tokenNo);
	        req.setOrderDate(DateUtils.changeDateString(message.getOrderDate(), "yyyyMMddHHmmss", "yyyyMMdd"));
	        req.setDate(CommonUtils.formatDate(new Date(), "yyyyMMdd HH:mm:ss"));
	        
	        msg.setId(this.getStringid());
	        msg.setSOQReqFather(req);
		}else{
		    PabExPayMessageSOQReqH req = new PabExPayMessageSOQReqH();
            req.setInstId(message.getMerID());
            req.setSerialNo(message.getOrderId());
            req.setSignNo(tokenNo);
            req.setOrderDate(DateUtils.changeDateString(message.getOrderDate(), "yyyyMMddHHmmss", "yyyyMMdd"));
            req.setDate(CommonUtils.formatDate(new Date(), "yyyyMMdd HH:mm:ss"));
            
            msg.setId(this.getStringid());
            msg.setSOQReqFather(req);
		}
		
		fastpay.setMessage(msg);
		
		return fastpay;
	}
	
	private void printResult(Result res, String desc){
		logger.error("pabexpay-" + desc +",返回对象RES为：" + LoggerMaskUtils.maskString(ToStringBuilder.reflectionToString(res.getModel("RES"))));
		if(StringUtils.isNotEmpty(res.getBankRetCode())){
			logger.error("pabexpay-" + desc +",errorCode:" + res.getBankRetCode() + ";errorMsg:" +res.getErrorMsg());
		}
		
	}
	
	private String objToXml(Object obj, Class clazz){
		XStream xs = new XStream();
        xs.autodetectAnnotations(true);
        xs.processAnnotations(clazz);
        OutputStream output = new ByteArrayOutputStream();
        xs.marshal(obj, new CompactWriter(new OutputStreamWriter(output)));
        return output.toString();
	}

	public String getStringid() {
		long time = System.currentTimeMillis();
		String id = String.valueOf(time);
		return id;
	}
	
	/**
	 * 查询tokenNo
	 * @param message
	 * @param tokenNo
	 * @return
	 */
	public String getTokenNo(ExPayBaseMessage message) {
        BisBankContractInfo bisBankContractInfo = new BisBankContractInfo();
        bisBankContractInfo.setBankCode(message.getBank());
        bisBankContractInfo.setBankBizProdCode(message.getBankBusProd().value);
        bisBankContractInfo.setIntrfaceChannelCode(message.getBankIntrChann().value);
        bisBankContractInfo.setCardNo(message.getCardNo());

        bisBankContractInfo = bankContractInfoDAO.getContractInfo(bisBankContractInfo);
        // 如果不存在信息，返回“”
        if (bisBankContractInfo == null) {
            return "";
        }  else {
        	return bisBankContractInfo.getContractNumber();
        }
    }
	
	/**
	 * 记录银行返回的tokenNo，用于协议支付
	 * @param message
	 * @param tokenNo  tokenNo
	 */
    public void recordTokenNo(ExPayBaseMessage message, String tokenNo) {
        BisBankContractInfo bisBankContractInfo = new BisBankContractInfo();
        bisBankContractInfo.setBankCode(message.getBank());
        bisBankContractInfo.setBankBizProdCode(message.getBankBusProd().value);
        bisBankContractInfo.setIntrfaceChannelCode(message.getBankIntrChann().value);
        bisBankContractInfo.setCardNo(message.getCardNo());

        bisBankContractInfo = bankContractInfoDAO.getContractInfo(bisBankContractInfo);
        // 如果不存在信息，新建
        if (bisBankContractInfo == null) {
            newContract(message, tokenNo);
        }  else {
        	//如果存在，就修改签约号
        	updateContract(message, tokenNo);
        }
    }
    
    /**
     * 新建签约信息
     *
     * @param message
     * @return
     */
    public void newContract(ExPayBaseMessage message, String tokenNo) {
        BisBankContractInfo bisBankContractInfo = new BisBankContractInfo();
        bisBankContractInfo.setBankCode(message.getBank());
        bisBankContractInfo.setBankBizProdCode(message.getBankBusProd().value);
        bisBankContractInfo.setIntrfaceChannelCode(message.getBankIntrChann().value);
        bisBankContractInfo.setCardNo(message.getCardNo());
        bisBankContractInfo.setContractState("01");
        bisBankContractInfo.setContractNumber(tokenNo);

        bankContractInfoDAO.insertBisBankContractInfo(bisBankContractInfo);
    }

    /**
     * 修改签约信息
     *
     * @param message
     * @return
     */
    public void updateContract(ExPayBaseMessage message, String tokenNo) {
        BisBankContractInfo bisBankContractInfo = new BisBankContractInfo();
        bisBankContractInfo.setBankCode(message.getBank());
        bisBankContractInfo.setBankBizProdCode(message.getBankBusProd().value);
        bisBankContractInfo.setIntrfaceChannelCode(message.getBankIntrChann().value);
        bisBankContractInfo.setCardNo(message.getCardNo());
        bisBankContractInfo.setContractState("01");
        bisBankContractInfo.setContractNumber(tokenNo);

        bankContractInfoDAO.updateBisBankContractInfo(bisBankContractInfo);
    }

	public void setpabExPayServiceClient(ExPayService pabExPayServiceClient) {
		this.pabExPayServiceClient = pabExPayServiceClient;
	}

	public void setPabConfig(Map<String, String> pabConfig) {
		this.pabConfig = pabConfig;
	}


	public void setBankOrderDAO(BankOrderDAO bankOrderDAO) {
		this.bankOrderDAO = bankOrderDAO;
	}

	public void setCpsGeneralReceiptOrderDAO(CpsGeneralReceiptOrderDAO cpsGeneralReceiptOrderDAO) {
		this.cpsGeneralReceiptOrderDAO = cpsGeneralReceiptOrderDAO;
	}

	

}